﻿using System;
using System.Collections.Generic;
using EzDesk.Apps.Data.ProjectModule;
using EzDesk.Apps.Interface.ProjectModule;
using EzDesk.Apps.Model.ProjectModule;
using EzDesk.Utility.Json;
using EzDesk.Utility.ServiceStatusInfo;

using System.Linq;
using System.Data.Linq;
using EzDesk.Utility.Web.Extensions;

namespace EzDesk.Apps.ServiceImpl.ProjectModule
{
    public partial class ProjectService : IProjectService
    {
        #region private methods

        private List<MemberInProject> getMemberInProjectList(string memberstring)
        {
            var members = _ezApiClient.GetSimpleUserInfos(memberstring);
            var memberInProjectList = new List<MemberInProject> { };
            memberInProjectList.AddRange(
                members.Select(simpleUser =>
                               new MemberInProject
                               {
                                   Id = Guid.NewGuid(),
                                   UserId = simpleUser.Id
                               })
                );
            return memberInProjectList;
        }


        private string FeedTemplateStr_project_newProjectForLeader = "project_newProjectForLeader";

        private void sendproject_newProjectForLeader(Project project)
        {
            var creatorName = _ezApiClient.GetSimpleUserById(project.CreatorId).Name;
            var projectId = project.Id.ToString();
            var projectName = project.Name;
            var feedData = new Dictionary<string, string>
                   {
                      {"creatorName", creatorName},
                      {"projectId", projectId},
                      {"projectName", projectName},
                   };

            _ezApiClient.SendFeed(
                 FeedTemplateStr_project_newProjectForLeader,
                 JsonHelper.ConvertToJson(feedData),
                 project.ChargePersonId.ToString()
                 );
        }


        #endregion

        public StatusInfo<List<Project>> GetProjects(Guid? userId, ProjectStatus[] projectStatus)
        {
            return StatusInfo.Wrap(() =>
            EzDeskProjectContainer.Go(container =>
            {
                IQueryable<Project> userProjets;
                //用userId进行过滤
                if (userId != null)
                    userProjets = container.MemberInProjectSet.Where(m => m.UserId == userId).Select(p => p.Project);
                else
                    userProjets = container.ProjectSet;


                //todo: 用ProjectStatus进行过滤
                //if (projectStatus != null)
                //    userProjets = projectStatus.Aggregate(userProjets,
                //        (current, status) => current.Where(p => p.ProjectStatus.StatusName == status.StatusName));

                foreach (var project in userProjets)
                {

                    container.LoadProperty(project, p => p.MemberInProject);
                }
                return userProjets.ToList();

            }));
        }

        public StatusInfo<List<Project>> GetProjectsForUser(Guid userId)
        {
            return StatusInfo.Wrap(() =>
            EzDeskProjectContainer.Go(container =>
            {
                var userProjets =
                    container.MemberInProjectSet.Where(
                        mip => mip.UserId == userId);

                foreach (var memberInProject in userProjets)
                {
                    container.LoadProperty(memberInProject,
                                        mip => mip.Project);
                }
                return
                    userProjets.Select(up => up.Project).ToList();
            }));
        }




        //Wait to make into Interface
        public StatusInfo<Project> GetFullProjectById(Guid projectId)
        {
            return StatusInfo.Wrap(
                () =>
                EzDeskProjectContainer.Go(
                    container =>
                    {
                        Action<Task> getTaskTree = null;
                        getTaskTree = (task) =>
                        {
                            container.LoadProperty(task,t=>t.Workitem);
                            container.LoadProperty(task, t => t.ChildTasks);
                            task.ChildTasks.ToList().ForEach(t=>getTaskTree(t));
                        };
                        

                        var project =container.ProjectSet.First(p => p.Id == projectId);
                        container.LoadProperty(project,p => p.MemberInProject);
                        container.LoadProperty(project,p => p.Workitem);
                        container.LoadProperty(project,p => p.Task);
                        project.Task.ToList().ForEach(t=>getTaskTree(t));

                        return project;
                    }));
        }

        public StatusInfo<Project> GetProjectById(Guid projectId)
        {
            return StatusInfo.Wrap(
                () =>
                EzDeskProjectContainer.Go(
                    container =>
                    {
                        var project =
                            container.ProjectSet.First(
                                p => p.Id == projectId);

                        container.LoadProperty(
                                    project,
                                    p => p.MemberInProject);

                        container.LoadProperty(
                                    project,
                                    p => p.Workitem);

                        return project;
                    }));
        }

        public StatusInfo<Project> CreateNewProject(Project project)
        {
            return StatusInfo.Wrap(()
            =>
            {
                return this.CreateProject(project);
            });
        }

        public StatusInfo<Project> CreateNewProjectWithTemplate(Project project,Guid projectTemplateId,DateTime startOn)
        {
            return StatusInfo.Wrap(()
                =>
                {
                    project = CreateProject(project);
                    ApplyProjectTemplate(project.Id,projectTemplateId);
                    ChangeProjectStartOn(project.Id,startOn);
                    return project;
                });
        }
        private void ApplyProjectTemplate(Guid projectId,Guid projectTemplateId)
        {
           using (var db = new EzDeskProjectContainer())
           {
               var projectTemplate = db.ProjectTemplateSet.First(pt => pt.Id == projectTemplateId);
               var templateProject = WebJsonExtension.FromJson<Project>(projectTemplate.TemplateData);
               var cacheIdMapper = templateProject.Task.ToDictionary(taskData => taskData.Id, taskData => Guid.NewGuid());

               foreach (var taskData in templateProject.Task)
               {
                   var task = new Task();
                   task.Id = cacheIdMapper[taskData.Id];
                   task.Name = taskData.Name;
                   if (taskData.MasterTaskId.HasValue)
                       task.MasterTaskId = cacheIdMapper[taskData.MasterTaskId.Value];

                   if (taskData.ParentTaskId.HasValue)
                       task.ParentTaskId = cacheIdMapper[taskData.ParentTaskId.Value];

                   task.ProjectId = projectId;
                   task.StartOn = taskData.StartOn;
                   task.Duration = taskData.Duration;
                   db.TaskSet.AddObject(task);
               }
               db.SaveChanges();
           }
        }

        private Project CreateProject(Project project)
        {
            using (var db = new EzDeskProjectContainer())
            {
                db.ContextOptions.ProxyCreationEnabled = false;
                db.ContextOptions.LazyLoadingEnabled = true;

                project.Id = Guid.NewGuid();
                project.Name = project.Name;
                project.Code = project.Code;
                project.CreateOn = DateTime.Now;
                project.MemberString = project.MemberString ?? "";

                project.Status = ProjectStatus.Ongoing;

                var memberInProjecList = getMemberInProjectList(project.MemberString);
                project.MemberInProject = memberInProjecList;
                db.ProjectSet.AddObject(project);
                db.SaveChanges();

                sendproject_newProjectForLeader(project);

                return project;
            }
         
        }


        public StatusInfo UpdateProjectBasicInfo(
            Guid projectId,
            string projectName, 
            string projectCode,
            string description)
        {
            return StatusInfo.Wrap(()
             =>
            {
                using (var db = new EzDeskProjectContainer())
                {
                    db.ContextOptions.ProxyCreationEnabled = false;

                    var project = db.ProjectSet.First(p => p.Id == projectId);
                    if (projectName != null)
                    {
                        project.Name = projectName;
                    }
                    if (projectCode != null)
                    {
                        project.Code = projectCode;
                    }
                    if (description != null)
                    {
                        project.Description = description;
                    }
                    db.SaveChanges();
                }
            }
            );
        }

        public StatusInfo UpdateProjectMember(Guid projectId, string memberString)
        {
            return StatusInfo.Wrap(()
             =>
            {
                using (var db = new EzDeskProjectContainer())
                {
                    db.ContextOptions.ProxyCreationEnabled = false;

                    var project = db.ProjectSet.First(p => p.Id == projectId);
                    project.MemberString = memberString;

                    var memberInProjects = db.MemberInProjectSet.Where(p => p.ProjectId == project.Id);
                    foreach (var memberInProject in memberInProjects)
                    {
                        db.MemberInProjectSet.DeleteObject(memberInProject);
                    }
                    db.SaveChanges();

                    project.MemberInProject = getMemberInProjectList(project.MemberString); ;

                    db.SaveChanges();

                }
            }
             );
        }

        public StatusInfo ChangeProjectStartOn(Guid projectId, DateTime newStartOn)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.GoAndSave(db =>
            {
                var tasks = db.TaskSet.Where(p => p.ProjectId == projectId);
                if(tasks.Count() == 0) return;
                var projectStartOn = tasks.Min(t => t.StartOn.Value);
                var differ = newStartOn - projectStartOn;
                foreach (var task in tasks)
                {
                    task.StartOn += differ;
                }
                db.SaveChanges();
            }));
        }

        public StatusInfo<ProjectTemplate> CreateProjectTemplate(ProjectTemplate projectTemplate)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.GoAndSave(db =>
            {
                projectTemplate.Id = Guid.NewGuid();
                projectTemplate.CreateOn = DateTime.Now;
                db.ProjectTemplateSet.AddObject(projectTemplate);
                db.SaveChanges();
                return projectTemplate;
            }));
        }
        public StatusInfo<List<ProjectTemplate>> GetProjectTemplateList()
        {
            return StatusInfo.Wrap(() => EzDeskProjectContainer.Go(db => db.ProjectTemplateSet.ToList()));
        }

        public StatusInfo RemoveProjectTemplate(Guid id)
        {
            return StatusInfo.Wrap(() => EzDeskProjectContainer.GoAndSave(db => 
                db.ProjectTemplateSet.DeleteObject(db.ProjectTemplateSet.FirstOrDefault(pt=>pt.Id == id))
            ));
        }
    }
}
